package com.iwhalecloud.citybrain.flow.platform.manager.application.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.CommonOperateLogHolder;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCommandDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpCommandResultDto;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.HttpTaskNoticeSettingDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.FlowRuTimeoutSettingVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowReTaskNoticeChannelVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowReTimeoutSettingVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.notice.FlowRuTaskNoticeChannelVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowReTaskNoticeChannelRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowReTimeoutSettingRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowRuTaskNoticeChannelRepository;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.FlowRuTimeoutSettingRepository;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.runtime.ProcessInstance;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Handler - 通知设置
 *
 * @author lilh
 * @date 2019-07-11 14:31
 */
@Service
public class TaskNoticeSettingCommandHandler extends AbstractCommandHandler {

    @Autowired
    private FlowReTimeoutSettingRepository flowReTimeoutSettingRepository;

    @Autowired
    private FlowRuTimeoutSettingRepository flowRuTimeoutSettingRepository;

    @Autowired
    private FlowReTaskNoticeChannelRepository flowReTaskNoticeChannelRepository;

    @Autowired
    private FlowRuTaskNoticeChannelRepository flowRuTaskNoticeChannelRepository;


    @Override
    public String handleType() {
        return TASK_NOTICE_SETTING;
    }

    @Override
    public HttpCommandResultDto handle(String params) {
        HttpTaskNoticeSettingDto httpTaskNoticeSettingDto = JSONObject.parseObject(params, HttpTaskNoticeSettingDto.class);
        return handle(httpTaskNoticeSettingDto);
    }

    @Override
    public HttpCommandResultDto handle(HttpCommandDto commandDto) {
        HttpTaskNoticeSettingDto httpTaskNoticeSettingDto = (HttpTaskNoticeSettingDto) commandDto;
        CommonOperateLogHolder.getContent().setRequest(httpTaskNoticeSettingDto);
        //1.参数校验，传递的流程实例是否有效
        ProcessInstance processInstance = resolveProcessInstance(httpTaskNoticeSettingDto.getProcessInstanceId());
        if (Objects.isNull(processInstance)) {
            logger.warn("流程实例不存在{}", httpTaskNoticeSettingDto.getProcessInstanceId());
            return resolveFailResult(httpTaskNoticeSettingDto.getProcessInstanceId(), ErrorCode.PROCESS_INSTANCE_NOT_EXIST.errMessage());
        }

        //1.校验是否传递节点信息，若无，则直接返回
        if (CollectionUtils.isEmpty(httpTaskNoticeSettingDto.getNodeItems())) {
            logger.warn("没有传递节点配置信息");
            return resolveFailResult(httpTaskNoticeSettingDto.getProcessInstanceId(), "没有传递节点配置信息");
        }


        //2.校验是否配置规则信息，若无，则直接返回


        //3. 查询部署通知表，若没有，则不处理
        //.查询实例通知表，若存在，则更新，若不存在，
        for (HttpTaskNoticeSettingDto.NodeItem nodeItem : httpTaskNoticeSettingDto.getNodeItems()) {

            //没有配置规则，则不处理
            if (CollectionUtils.isEmpty(nodeItem.getNoticeItems())) {
                continue;
            }
            //3.1 查询部署通知表，若不存在，则不处理,先以deployId和activityId查询该节点是否有配置
            List<FlowReTimeoutSettingVo> reTimeoutSettings = resolveReTimeoutSettings(processInstance, nodeItem.getNodeCode());
            //该节点在系统中没有配置规则，则不处理
            if (reTimeoutSettings.isEmpty()) {
                continue;
            }

            //系统配置中通知类型与配置的对应关系，预警可能对应多条，故先进行分组
            Map<String, List<FlowReTimeoutSettingVo>> typeToSettingMap = reTimeoutSettings.stream().collect(Collectors.groupingBy(FlowReTimeoutSettingVo::getType));

            //通知类型与配置的对应关系
            Map<String, HttpTaskNoticeSettingDto.NoticeItem> noticeTypeToNoticeItemMap = nodeItem.getNoticeItems().stream().collect(Collectors.toMap(HttpTaskNoticeSettingDto.NoticeItem::getNoticeType, item -> item));

            for (String noticeType : noticeTypeToNoticeItemMap.keySet()) {
                //接口配置中指定的通知类型在部署通知配置表中不存在，则不处理
                if (CollectionUtils.isEmpty(typeToSettingMap.get(noticeType))) {
                    continue;
                }

                //若存在，查找实例通知配置表中是否存在，若存在，则更新，若不存在，则新增
                List<FlowRuTimeoutSettingVo> ruTimeoutSettings = resolveRuTimeoutSettings(processInstance, nodeItem.getNodeCode(), noticeType);
                if (CollectionUtils.isEmpty(ruTimeoutSettings)) {
                    //实例配置表中不存在，则从部署配置表中拷贝数据到实例配置中
                    copyReSettingToRuSetting(typeToSettingMap.get(noticeType), noticeTypeToNoticeItemMap.get(noticeType), processInstance);
                } else {
                    //更新配置
                    updateRuSettings(ruTimeoutSettings, noticeTypeToNoticeItemMap.get(noticeType), processInstance);
                }
            }

        }


        return resolveResult(processInstance, null, TASK_NOTICE_SETTING_SUCCESS);
    }

    private void updateRuSettings(List<FlowRuTimeoutSettingVo> ruTimeoutSettings, HttpTaskNoticeSettingDto.NoticeItem noticeItem, ProcessInstance processInstance) {
        for (FlowRuTimeoutSettingVo ruTimeoutSetting : ruTimeoutSettings) {
            boolean needUpdate = false;
            if (!Objects.equals(ruTimeoutSetting.getIsNotice(), noticeItem.getNotice())) {
                needUpdate = true;
                ruTimeoutSetting.setIsNotice(noticeItem.getNotice());
            }
            if (StringUtils.isNotBlank(noticeItem.getNoticeTitle())) {
                needUpdate = true;
                ruTimeoutSetting.setTitle(noticeItem.getNoticeTitle());
            }
            if (StringUtils.isNotBlank(noticeItem.getNoticeContent())) {
                needUpdate = true;
                ruTimeoutSetting.setContent(noticeItem.getNoticeContent());
            }

            if (Objects.equals("N", ruTimeoutSetting.getIsNotice())) {
                if (needUpdate) {
                    //
                    flowRuTimeoutSettingRepository.updateFlowRuTimeoutSetting(ruTimeoutSetting);
                }
                continue;
            }

        }
    }

    private void copyReSettingToRuSetting(List<FlowReTimeoutSettingVo> flowReTimeoutSettings, HttpTaskNoticeSettingDto.NoticeItem noticeItem, ProcessInstance processInstance) {

        for (FlowReTimeoutSettingVo setting : flowReTimeoutSettings) {
            FlowRuTimeoutSettingVo ruTimeoutSetting = new FlowRuTimeoutSettingVo();
            //1.先将主表的数据复制过去
            BeanUtils.copyProperties(setting, ruTimeoutSetting, "id", "parentId", "createTime", "updateTime");

            ruTimeoutSetting.setProcInstId(processInstance.getProcessInstanceId());

            //2.处理是否通知，通知标题，内容等配置项
            if (StringUtils.isNotBlank(noticeItem.getNotice())) {
                ruTimeoutSetting.setIsNotice(noticeItem.getNotice());
            }
            if (StringUtils.isNotBlank(noticeItem.getNoticeTitle())) {
                ruTimeoutSetting.setTitle(noticeItem.getNoticeTitle());
            }
            if (StringUtils.isNotBlank(noticeItem.getNoticeContent())) {
                ruTimeoutSetting.setContent(noticeItem.getNoticeContent());
            }
            //todo 专门处理一下间隔时间duration

            //如果设置了为不通知，则后续逻辑不再处理
            if (Objects.equals(noticeItem.getNotice(), "N")) {
                flowRuTimeoutSettingRepository.insertFlowRuTimeoutSetting(ruTimeoutSetting);
                continue;
            }

            //3.判断是否是预警通知，特殊处理
            if (isWarningNotice(setting)) {
                if (CollectionUtils.isEmpty(noticeItem.getWarningItems())) {
                    continue;
                }
                Map<String, HttpTaskNoticeSettingDto.WarningItem> warningCodeToWarningItemMap = noticeItem.getWarningItems().stream().collect(Collectors.toMap(HttpTaskNoticeSettingDto.WarningItem::getWarningCode, item -> item));
                HttpTaskNoticeSettingDto.WarningItem warningItem = warningCodeToWarningItemMap.get(setting.getWarningCode());
                if (Objects.isNull(warningItem)) {
                    continue;
                }

                flowRuTimeoutSettingRepository.insertFlowRuTimeoutSetting(ruTimeoutSetting);

                //3.2 查询人员，渠道表
                doInsertDataToRuTaskNoticeChannel(processInstance, setting, ruTimeoutSetting, warningItem.getNotifiers(), warningItem.getNoticeChannels());


            } else {
                flowRuTimeoutSettingRepository.insertFlowRuTimeoutSetting(ruTimeoutSetting);
                //非预警,处理noticeItem中的人员和渠道信息
                doInsertDataToRuTaskNoticeChannel(processInstance, setting, ruTimeoutSetting, noticeItem.getNotifiers(), noticeItem.getNoticeChannels());
            }
        }
    }

    private void doInsertDataToRuTaskNoticeChannel(ProcessInstance processInstance, FlowReTimeoutSettingVo setting, FlowRuTimeoutSettingVo ruTimeoutSetting, Set<String> sourceNotifiers, Set<String> sourceChannels) {
        //3.2 查询人员，渠道表
        List<FlowReTaskNoticeChannelVo> flowReTaskNoticeChannels = resolveReTaskNoticeChannels(setting);

        Set<String> defaultChannels = null;
        Set<String> defaultNotifiers = null;
        if (CollectionUtils.isNotEmpty(flowReTaskNoticeChannels)) {
            defaultNotifiers = flowReTaskNoticeChannels.stream().map(FlowReTaskNoticeChannelVo::getStaCode).collect(Collectors.toSet());
            defaultChannels = flowReTaskNoticeChannels.stream().map(FlowReTaskNoticeChannelVo::getChannel).collect(Collectors.toSet());
        }

        insertDataToRuTaskNoticeChannel(resolveNotifiersOrChannels(sourceChannels, defaultChannels), resolveNotifiersOrChannels(sourceNotifiers, defaultNotifiers), ruTimeoutSetting, processInstance);
    }

    private Set<String> resolveNotifiersOrChannels(Set<String> datas, Set<String> defaultDatas) {
        if (CollectionUtils.isNotEmpty(datas)) {
            return datas;
        }

        if (CollectionUtils.isNotEmpty(defaultDatas)) {
            return defaultDatas;
        }
        return new HashSet<>();
    }


    private void insertDataToRuTaskNoticeChannel(Set<String> channels, Set<String> notifiers, FlowRuTimeoutSettingVo ruTimeoutSetting, ProcessInstance processInstance) {
        for (String notifier : notifiers) {
            for (String channel : channels) {
                FlowRuTaskNoticeChannelVo entity = new FlowRuTaskNoticeChannelVo();
                entity.setActivityId(ruTimeoutSetting.getActivityId());
                entity.setRuTimeoutSettingId(ruTimeoutSetting.getId());
                entity.setProcInstId(processInstance.getProcessInstanceId());
                entity.setSource("T");
                entity.setStaCode(notifier);
                entity.setChannel(channel);
                flowRuTaskNoticeChannelRepository.insertFlowRuTaskNoticeChannel(entity);
            }

        }
    }

    private List<FlowReTaskNoticeChannelVo> resolveReTaskNoticeChannels(FlowReTimeoutSettingVo setting) {
        List<FlowReTaskNoticeChannelVo> result = new ArrayList<>();
        result.addAll(flowReTaskNoticeChannelRepository.listFlowReTaskNoticeChannel(setting.getId()));
        return result;
    }

    private boolean isWarningNotice(FlowReTimeoutSettingVo setting) {
        return StringUtils.isNotBlank(setting.getWarningCode());
    }

    private List<FlowRuTimeoutSettingVo> resolveRuTimeoutSettings(ProcessInstance processInstance, String nodeCode, String noticeType) {
        List<FlowRuTimeoutSettingVo> result = new ArrayList<>();
        if (StringUtils.isBlank(nodeCode) || StringUtils.isBlank(noticeType)) {
            return result;
        }
        result.addAll(flowRuTimeoutSettingRepository.listFlowRuTimeoutSetting(processInstance.getProcessInstanceId(), nodeCode, noticeType));
        return result;
    }

    private List<FlowReTimeoutSettingVo> resolveReTimeoutSettings(ProcessInstance processInstance, String nodeCode) {
        List<FlowReTimeoutSettingVo> result = new ArrayList<>();
        if (StringUtils.isBlank(nodeCode)) {
            return result;
        }
        result.addAll(flowReTimeoutSettingRepository.listFlowReTimeoutSetting(processInstance.getDeploymentId(), nodeCode));
        return result;
    }
}
